/*
** $Id: lgc.h $
** GC(垃圾回收)
** See Copyright Notice in lua.h
*/

#ifndef lgc_h
#define lgc_h


#include "lobject.h"
#include "lstate.h"

/*
** 可收集对象可能有以下三种颜色中的一种:
** 白色(white): 这个对象没有被标记
** 灰色(gray): 这个对象被标记了，但是他的引用可能没有被标记
** 黑色(black): 这个对象和他的所有引用都被标记了
** GC的主要不变式：标记对象时，黑色对象永远不会指向白色对象
** 而且，所有的灰色对象必须在一个“灰色列表”(gray, grayagain, weak, allweak, ephemeron)中，以便在收集周期结束之前能再次被访问
** (打开upvalue时这个规则的例外)，这些列表只在不变式被强制执行时有意义，例如在标记阶段。在其它阶段，如清除阶段（sweep phase），这些列表并没有特别的意义
** barrier(屏障): 屏障的作用就是为了防止一致性原则被破坏的，它是通过重新修改颜色的方式来实现的
*/


/*
** GC状态
*/

/*
** 1）标记阶段：算法开始前会把所有对象设置为未标记状态，然后对Lua运行环境的某些根结点进行初始的标记，然后开始根据对象引用关系递归遍历它们所有的子对象，并把遍历到的对象也进行标记
** 2）清除阶段：当标记阶段结束后，则进入清除阶段，此时仍没有被标记到的对象，就是从根结点无法通过引用关系访问的对象，就是我们要找的已经不再需要的内存垃圾，然后就对它们的清除，释放它们的内存
*/
#define GCSpropagate	0	// 传播
#define GCSenteratomic	1	// 进入原子阶段
#define GCSatomic	2	// 原子阶段
#define GCSswpallgc	3	// 清除“regular"对象
#define GCSswpfinobj	4	// 清除有终结器的对象
#define GCSswptobefnz	5	// 清除尚未执行终结器的对象
#define GCSswpend	6	// 清除结束
#define GCScallfin	7	// 调用终结器
#define GCSpause	8	// 暂停

#define issweepphase(g)  \
	(GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend)

/*
** 主要不变式(白色对象不等你指向黑色对象)是否需要被保持，一次收集中，清除阶段可能会打破不变式，因为变为白色的对象可能仍然指向黑色对象
** 当清除阶段结束并且所有对象再次为白色时，将恢复不变式
*/
#define keepinvariant(g)	((g)->gcstate <= GCSatomic)


/*
** 一些实用位技巧
*/
#define resetbits(x,m)		((x) &= cast_byte(~(m)))
#define setbits(x,m)		((x) |= (m))
#define testbits(x,m)		((x) & (m))
#define bitmask(b)		(1<<(b))
#define bit2mask(b1,b2)		(bitmask(b1) | bitmask(b2))
#define l_setbit(x,b)		setbits(x, bitmask(b))
#define resetbit(x,b)		resetbits(x, bitmask(b))
#define testbit(x,b)		testbits(x, bitmask(b))


/*
** 位的布局使用’marked' 前3位用于分代模式的‘age'，最后一位用于测试
** 由于清除阶段是增量式的，需要防止清除阶段产生的新对象被错误回收，所以使用两种白色(一种需要被回收，一种不需要)
*/
#define WHITE0BIT	3  /* 对象是白色(type 0) */
#define WHITE1BIT	4  /* 对象是白色(type 1) */
#define BLACKBIT	5  /* 对象是黑色 */
#define FINALIZEDBIT	6  /* 对象是否有终结器(析构标记，定义了析构器的对象拥有这个析构标记，代表释放之前需要先调用__GC元方法) */

#define TESTBIT		7


#define WHITEBITS	bit2mask(WHITE0BIT, WHITE1BIT)


#define iswhite(x)      testbits((x)->marked, WHITEBITS)
#define isblack(x)      testbit((x)->marked, BLACKBIT)
#define isgray(x)  /* 既不是白色也不是黑色(000) */  \
	(!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT)))

#define tofinalize(x)	testbit((x)->marked, FINALIZEDBIT)

#define otherwhite(g)	((g)->currentwhite ^ WHITEBITS)
#define isdeadm(ow,m)	((m) & (ow))
#define isdead(g,v)	isdeadm(otherwhite(g), (v)->marked)

#define changewhite(x)	((x)->marked ^= WHITEBITS)
#define nw2black(x)  \
	check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT))

#define luaC_white(g)	cast_byte((g)->currentwhite & WHITEBITS)


/* 分代模式中对象年龄 */
#define G_NEW		0	/* 当前周期创建 */
#define G_SURVIVAL	1	/* 上一个周期创建 */
#define G_OLD0		2	/* 2个周期后变老 在这个周期中被标记为旧的由frw设置的屏障(屏障：用于在GC过程中保护某些对象不被错误回收，可以确保在收集器运行时，某些重要的引用关系不被破坏) marked old by frw. barrier in this cycle */
#define G_OLD1		3	/* 1个周期后变老 第一个完整周期的老对象 first full cycle as old */
#define G_OLD		4	/* really old object (not to be visited) */
#define G_TOUCHED1	5	/* 这个周期被touched的老对象 */
#define G_TOUCHED2	6	/* 上一周期被touched的老对象 */

#define AGEBITS		7  /* 所有的年龄位 (111) */

#define getage(o)	((o)->marked & AGEBITS)
#define setage(o,a)  ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a))
#define isold(o)	(getage(o) > G_SURVIVAL)

#define changeage(o,f,t)  \
	check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t)))


/* 分代GC参数的默认值 */
#define LUAI_GENMAJORMUL         100
#define LUAI_GENMINORMUL         20

/* 等待内存两倍后开始新的周期 */
#define LUAI_GCPAUSE    200

/*
** 一些GC参数通过 / 4 存储以允许最大值 > 1023
*/
#define getgcparam(p)	((p) * 4)
#define setgcparam(p,v)	((p) = (v) / 4)

#define LUAI_GCMUL      100

/* 下一个GC步骤前分配内存 (log2) */
#define LUAI_GCSTEPSIZE 13      /* 8 KB */


/*
** 检测声明的GC模式是否是分代模式。当是分代模式时，收集器可以暂时进入增量模式以提高性能，这是由 'g->lastatomic != 0' 发出的信号
*/
#define isdecGCmodegen(g)	(g->gckind == KGC_GEN || g->lastatomic != 0)


/*
** 控制GC是否运行中:
*/
#define GCSTPUSR	1  /* 当GC被用户停止时为true */
#define GCSTPGC		2  /* 当GC自己停止时为true */
#define GCSTPCLS	4  /* 当关闭LuaState时为true */
#define gcrunning(g)	((g)->gcstp == 0)


/*
** 当债务变成正数时，做一步收集. 'pre'/'pos'允许在需要时进行一些调整
** 宏 'condchangemem'用于重度测试(每次有机会都强制执行一次完整的gc)
*/
#define luaC_condGC(L,pre,pos) \
	{ if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \
	  condchangemem(L,pre,pos); }

#define luaC_checkGC(L)		luaC_condGC(L,(void)0,(void)0)

#define luaC_objbarrier(L,p,o) (  \
	(isblack(p) && iswhite(o)) ? \
	luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))

#define luaC_barrier(L,p,v) (  \
	iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0))

#define luaC_objbarrierback(L,p,o) (  \
	(isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0))

#define luaC_barrierback(L,p,v) (  \
	iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0))

LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
LUAI_FUNC void luaC_freeallobjects (lua_State *L);
LUAI_FUNC void luaC_step (lua_State *L);
LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz,
                                                 size_t offset);
LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
LUAI_FUNC void luaC_changemode (lua_State *L, int newmode);


#endif
